﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net;
using Lextm.SharpSnmpLib;
using Lextm.SharpSnmpLib.Security;
using Lextm.SharpSnmpLib.Messaging;

namespace SnmpForm.SnmpWrapper
{
    public class SnmpException : Exception
    {
        public SnmpException(string msg)
            : base(msg)
        {
        }
    }

    public class SnmpNullException : SnmpException
    {
        public SnmpNullException(string msg)
            : base(msg)
        {
        }
    }

    public class SnmpErrorException : SnmpException
    {
        public SnmpErrorException(string msg)
            : base(msg)
        {
        }
    }

    public class Snmp
    {
        IPEndPoint _receiver = null;
        string _user;
        IAuthenticationProvider _auth = null;
        IPrivacyProvider _priv = null;
        public Snmp(string dest, string user, string passwd)
        {
            _receiver = new IPEndPoint(IPAddress.Parse(dest), 161);
            _user = user;
            _auth = GetAuthenticationProviderByName("MD5", passwd);
            _priv = GetIPrivacyProvider(_auth, Levels.Privacy, "ookprivacy");
        }

        private static IAuthenticationProvider GetAuthenticationProviderByName(string authentication, string phrase)
        {
            if (authentication.ToUpperInvariant() == "MD5")
            {
                return new MD5AuthenticationProvider(new OctetString(phrase));
            }

            if (authentication.ToUpperInvariant() == "SHA")
            {
                return new SHA1AuthenticationProvider(new OctetString(phrase));
            }

            throw new ArgumentException("unknown name", "authentication");
        }

        private static IPrivacyProvider GetIPrivacyProvider(
            IAuthenticationProvider auth, 
            Levels level,
            string privPhrase)
        {

            IPrivacyProvider priv;
            if ((level & Levels.Privacy) == Levels.Privacy)
            {
                priv = new DESPrivacyProvider(new OctetString(privPhrase), auth);
            }
            else
            {
                priv = new DefaultPrivacyProvider(auth);
            }
            return priv;
        }

        public IList<Variable> get(IList<string> oids, int timeout=1000)
        {
            IList<Variable> listResult = null;
            List<Variable> vList = new List<Variable>();
            foreach (string oid in oids)
            {
                Variable test = new Variable(new ObjectIdentifier(oid));
                vList.Add(test);
            }

            //Discovery discovery = Messenger.NextDiscovery;
            Discovery discovery = Messenger.GetNextDiscovery(SnmpType.GetBulkRequestPdu);
            ReportMessage report = discovery.GetResponse(timeout, _receiver);

            GetRequestMessage request = new GetRequestMessage(VersionCode.V3, Messenger.NextMessageId, Messenger.NextRequestId, new OctetString(_user), vList, _priv, Messenger.MaxMessageSize, report);

            ISnmpMessage response = request.GetResponse(timeout, _receiver);
            if (response is ReportMessage)
            {
                if (response.Pdu().Variables.Count == 0)
                {
                    throw new Exception("wrong report message received");
                }

                var id = response.Pdu().Variables[0].Id;
                if (id != Messenger.NotInTimeWindow)
                {
                    var error = id.GetErrorMessage();
                    throw new SnmpErrorException(error);
                }

                // according to RFC 3414, send a second request to sync time.
                request = new GetRequestMessage(VersionCode.V3, Messenger.NextMessageId, Messenger.NextRequestId, new OctetString(_user), vList, _priv, Messenger.MaxMessageSize, report);
                response = request.GetResponse(timeout, _receiver);
            }
            else if (response.Pdu().ErrorStatus.ToInt32() != 0) // != ErrorCode.NoError
            {
                throw ErrorException.Create(
                          "error in response",
                          _receiver.Address,
                          response);
            }

            listResult = response.Pdu().Variables;
            return listResult;
        }

        public string get(string oid, int timeout=1000)
        {
            string strRet = null;

            List<string> oids = new List<string>();
            oids.Add(oid);

            IList<Variable> vars = get(oids);
            if (vars.Count == 1)
            {
                Variable var = vars[0];
                if (var.Data.TypeCode == SnmpType.NoSuchObject)
                {
                    throw new SnmpNullException(var.Id.ToString());
                }
                strRet = var.Data.ToString();
            }
            else
            {
            }
            return strRet;
        }

        public bool set(string oid, SnmpType type, object value, int timeout=1000)
        {
            List<Variable> vList = new List<Variable>();
            ISnmpData data = null;
            switch(type){
                case SnmpType.OctetString:
                    data = new OctetString((string)value);
                    break;
                case SnmpType.Integer32:
                    data = new Integer32((int)value);
                    break;
                default: 
                    break;
            }
            Variable test = new Variable(new ObjectIdentifier(oid), data);
            vList.Add(test);

            //Discovery discovery = Messenger.NextDiscovery;
            Discovery discovery = Messenger.GetNextDiscovery(SnmpType.GetNextRequestPdu);
            ReportMessage report = discovery.GetResponse(timeout, _receiver);

            SetRequestMessage request = new SetRequestMessage(VersionCode.V3, Messenger.NextMessageId, Messenger.NextRequestId, new OctetString(_user), vList, _priv, Messenger.MaxMessageSize, report);

            ISnmpMessage response = request.GetResponse(timeout, _receiver);

            if (response.Pdu().ErrorStatus.ToInt32() != 0) // != ErrorCode.NoError
            {
                return false;
            }

            var vars = response.Pdu().Variables;
            if (vars.Count != 1)
            {
                return false;
            }

            //通过判断返回的OID是不是请求OID,来确认设置是否成功
            Variable v = vars[0];
            if (v.Id.Equals(new ObjectIdentifier(oid)))
            {
            }
            else
            {
                return false;
            }

            return true;
        }

        public void GetTable()
        {
        }
    }
}
